package com.zyx.concurrentdemo.c06cas;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author zyx
 * @since 2021/10/17 17:30
 * desc: cas入门案例
 */
public class TestWithDraw {
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            Account acount = new AccountUnsafe(10000);
            Account.demo(acount);
        }

        System.out.println("=====>>>>> break");

        for (int i = 0; i < 5; i++) {
            Account casAccount = new AccountUnsafe(10000);
            Account.demo(casAccount);
        }

    }
}


class AccountCasSafe implements Account {

    private AtomicInteger balance;

    public AccountCasSafe(Integer balance) {
        this.balance = new AtomicInteger(balance);
    }

    @Override
    public Integer getBalance() {
        // 保证多线程操作的顺序性
        return balance.get();
    }

    @Override
    public void withdraw(Integer amount) {
        // cas解决线程安全问题
        while (true) {
            int prev = balance.get();
            int next = prev - amount;
            if (balance.compareAndSet(prev, next)) {
                break;
            }
        }
    }
}


class AccountUnsafe implements Account {

    private Integer balance;

    public AccountUnsafe(Integer balance) {
        this.balance = balance;
    }

    @Override
    public Integer getBalance() {
        // 保证多线程操作的顺序性
        synchronized (this) {
            return this.balance;
        }
    }

    @Override
    public void withdraw(Integer amount) {
        // 加锁可以避免并发问题
        // 另一方面, 枷锁会造成线程堵塞, 堵塞的时间取决于临界区代码执行的时间, 使得使用加锁的性能不高
        synchronized (this) {
            this.balance -= amount;
        }
    }
}

interface Account {
    /**
     * 获取金额的方法
     * @return 获取金额
     */
    Integer getBalance();

    /**
     * 存钱的方法
     * @param amount 存入的金额
     */
    void withdraw(Integer amount);

    static void demo(Account account) {
        List<Thread> list = new ArrayList<>();
        long start = System.nanoTime();
        // 1000个线程同时取款
        for (int i = 0; i < 1000; i++) {
            list.add(new Thread(() -> {
                account.withdraw(10);
            }));
        }
        list.forEach(Thread::start);
        list.forEach(t -> {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        long end = System.nanoTime();
        System.out.println(account.getBalance()
                + " cost: " + (end - start) / 1000_000 + " ms");
    }
}